
import pandas as pd
import numpy as np
from jinja2 import Environment, FileSystemLoader


def compare_dataframes(df1, df2, index_cols):
    """
    比较两个DataFrame，识别新增行、删除行和修改行
    """
    # 确保索引列存在
    for col in index_cols:
        if col not in df1.columns or col not in df2.columns:
            raise ValueError(f"索引列 '{col}' 不存在于数据中")

    # 设置索引
    df1_indexed = df1.set_index(index_cols).sort_index()
    df2_indexed = df2.set_index(index_cols).sort_index()

    # 识别新增行和删除行
    added_rows = df2_indexed.index.difference(df1_indexed.index)
    deleted_rows = df1_indexed.index.difference(df2_indexed.index)

    # 识别修改行
    common_index = df1_indexed.index.intersection(df2_indexed.index)
    df1_common = df1_indexed.loc[common_index]
    df2_common = df2_indexed.loc[common_index]

    # 比较共同行的差异
    ne_stacked = (df1_common != df2_common).stack()
    changed = ne_stacked[ne_stacked]
    changed_rows = changed.index.get_level_values(0).unique()

    # 提取差异数据
    added_df = df2.loc[df2.set_index(index_cols).index.isin(added_rows)].reset_index(drop=True)
    deleted_df = df1.loc[df1.set_index(index_cols).index.isin(deleted_rows)].reset_index(drop=True)

    # 准备修改行数据
    changes = []
    for idx in changed_rows:
        # 获取原始行
        old_row = df1_common.loc[[idx]].reset_index()
        new_row = df2_common.loc[[idx]].reset_index()

        # 识别具体修改的列
        diff_cols = {}
        for col in df1.columns:
            if col in index_cols:
                continue

            old_val = old_row[col].iloc[0]
            new_val = new_row[col].iloc[0]

            # 处理NaN值
            if pd.isna(old_val) and pd.isna(new_val):
                continue

            # 比较值是否不同
            if (pd.isna(old_val) != pd.isna(new_val)) or (
                    not pd.isna(old_val) and not pd.isna(new_val) and old_val != new_val):
                diff_cols[col] = {
                    'old': old_val,
                    'new': new_val
                }

        changes.append({
            'index': {col: old_row[col].iloc[0] for col in index_cols},
            'old': old_row.iloc[0].to_dict(),
            'new': new_row.iloc[0].to_dict(),
            'changed_columns': diff_cols
        })

    # 限制差异点数量
    added_df = added_df.head(1000)
    deleted_df = deleted_df.head(1000)
    changes = changes[:1000]

    return {
        'added': added_df,
        'deleted': deleted_df,
        'modified': changes,
        'stats': {
            'total_added': len(added_df),
            'total_deleted': len(deleted_df),
            'total_modified': len(changes),
            'df1_rows': len(df1),
            'df2_rows': len(df2),
            'common_rows': len(common_index),
            'index_columns': index_cols
        },
        'columns': list(df1.columns)
    }


def generate_html_report(comparison_result, output_path='report.html', page_size=1000):
    """
    生成HTML差异报告
    """
    # 设置Jinja2环境
    env = Environment(loader=FileSystemLoader('.'))
    template = env.get_template('report_template.html')

    # 准备分页数据
    added_pages = paginate_dataframe(comparison_result['added'], page_size)
    deleted_pages = paginate_dataframe(comparison_result['deleted'], page_size)
    modified_pages = paginate_list(comparison_result['modified'], page_size)

    # 渲染模板
    html_output = template.render(
        stats=comparison_result['stats'],
        added_pages=added_pages,
        deleted_pages=deleted_pages,
        modified_pages=modified_pages,
        columns=comparison_result['columns'],
        page_size=page_size
    )

    # 保存报告
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(html_output)

    return output_path


def paginate_dataframe(df, per_page):
    """将DataFrame分页"""
    pages = []
    total_pages = (len(df) // per_page) + (1 if len(df) % per_page > 0 else 0)

    for i in range(total_pages):
        start_idx = i * per_page
        end_idx = start_idx + per_page
        page_data = df.iloc[start_idx:end_idx]
        pages.append({
            'number': i + 1,
            'data': page_data.to_dict('records'),
            'active': (i == 0)
        })

    return pages


def paginate_list(lst, per_page):
    """将列表分页"""
    pages = []
    total_pages = (len(lst) // per_page) + (1 if len(lst) % per_page > 0 else 0)

    for i in range(total_pages):
        start_idx = i * per_page
        end_idx = start_idx + per_page
        page_data = lst[start_idx:end_idx]
        pages.append({
            'number': i + 1,
            'data': page_data,
            'active': (i == 0)
        })

    return pages


# 示例使用
if __name__ == "__main__":
    # 生成示例数据
    np.random.seed(42)
    data1 = {
        'id': list(range(1, 50001)),
        'name': [f'Name_{i}' for i in range(1, 50001)],
        'value': list(np.random.rand(50000)),
        'category': list(np.random.choice(['A', 'B', 'C'], 50000)),
        'status': list(np.random.choice(['Active', 'Inactive'], 50000)),
        'score': list(np.random.randint(1, 100, 50000))
    }

    # 创建有差异的数据
    data2 = {}
    for key in data1:
        # 复制原始数据
        data2[key] = data1[key].copy()

    # 修改部分数据
    n = len(data2['id'])
    for i in range(n):
        if i % 10 == 0:
            data2['value'][i] += 0.1
        if i % 5 == 0 and data2['status'][i] == 'Active':
            data2['status'][i] = 'Inactive'
        if i % 7 == 0:
            data2['score'][i] += 5

    # 添加新行
    new_ids = range(50001, 51001)
    n_new = len(new_ids)
    data2['id'].extend(new_ids)
    data2['name'].extend([f'New_Name_{i}' for i in new_ids])
    data2['value'].extend(list(np.random.rand(n_new)))
    data2['category'].extend(list(np.random.choice(['D', 'E'], n_new)))
    data2['status'].extend(list(np.random.choice(['Active', 'Pending'], n_new)))
    data2['score'].extend(list(np.random.randint(80, 100, n_new)))

    # 删除部分行，使数据量为50500行
    for key in data2:
        data2[key] = data2[key][:50500]

    df1 = pd.DataFrame(data1)
    df2 = pd.DataFrame(data2)

    # 比较数据 (基于单列或多列索引)
    index_columns = ['id']  # 可以修改为 ['id', 'name'] 进行多列索引
    comparison_result = compare_dataframes(df1, df2, index_columns)

    # 生成报告
    generate_html_report(comparison_result)
